#import <libkern/OSAtomic.h>
#import "BCWeakTimer.h"

#if OS_OBJECT_USE_OBJC
    #define bc_gcd_property_qualifier strong
    #define bc_release_gcd_object(object)
#else
    #define bc_gcd_property_qualifier assign
    #define bc_release_gcd_object(object) dispatch_release(object)
#endif

@interface BCWeakTimer () {
    struct{
        uint32_t timerIsInvalidated;
        uint32_t timerIsPaused;
        uint32_t timerIsFired;
    } _timerFlags;
}

@property (nonatomic, assign) NSTimeInterval timeInterval;

@property (nonatomic, weak) id target;
@property (nonatomic, assign) SEL selector;
@property (nonatomic, copy) void (^ block)(BCWeakTimer *timer);

@property (nonatomic, strong) id userInfo;
@property (nonatomic, assign) BOOL isRepeats;
@property (nonatomic, assign) BOOL isTarget;

@property (nonatomic, bc_gcd_property_qualifier) dispatch_queue_t privateSerialQueue;

@property (nonatomic, bc_gcd_property_qualifier) dispatch_source_t timer;

@end

@implementation BCWeakTimer
@synthesize tolerance = _tolerance;
///MARK: - dealloc
- (void)dealloc
{
    NSLog(@"%@ dealloc", self.description);

    [self invalidate];

    bc_release_gcd_object(_privateSerialQueue);
}

///MARK: - init
- (instancetype)initWithTimeInterval:(NSTimeInterval)timeInterval
                            userInfo:(id)userInfo
                             repeats:(BOOL)repeats
                       dispatchQueue:(dispatch_queue_t)dispatchQueue {
    if (self = [super init]) {
        // 默认主线程
        if (!dispatchQueue) {
            dispatchQueue = dispatch_get_main_queue();
        }

        self.timeInterval = timeInterval;
        self.userInfo = userInfo;
        self.isRepeats = repeats;
        // 不需要使用set
        _tolerance = 0.1;

        NSString *privateQueueName = [NSString stringWithFormat:@"com.mindsnacks.bcweaktimer.%p", self];
        self.privateSerialQueue = dispatch_queue_create([privateQueueName cStringUsingEncoding:NSASCIIStringEncoding], DISPATCH_QUEUE_SERIAL);
        // 将 privateSerialQueue 与 dispatchQueue 串行执行
        dispatch_set_target_queue(self.privateSerialQueue, dispatchQueue);

        self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,
                                            0,
                                            0,
                                            self.privateSerialQueue);

        __weak typeof(self) weakSelf = self;
        dispatch_source_set_event_handler(self.timer, ^{
            __strong typeof(weakSelf) self = weakSelf;
            [self timerCallback];
        });
        dispatch_resume(self.timer);
    }

    return self;
}

- (void)resetTimerProperties
{
    int64_t intervalInNanoseconds = (int64_t)(self.timeInterval * NSEC_PER_SEC);
    int64_t toleranceInNanoseconds = (int64_t)(self.tolerance * NSEC_PER_SEC);

    dispatch_source_set_timer(self.timer,
                              dispatch_time(DISPATCH_TIME_NOW, intervalInNanoseconds),
                              (uint64_t)intervalInNanoseconds,
                              toleranceInNanoseconds
                              );
}

///MARK: - public
+ (instancetype)timerWithTimeInterval:(NSTimeInterval)timeInterval
                               target:(id)target
                             selector:(SEL)selector
                             userInfo:(nullable id)userInfo
                              repeats:(BOOL)repeats
                        dispatchQueue:(nullable dispatch_queue_t)dispatchQueue {
    NSParameterAssert(target);
    NSParameterAssert(target);

    BCWeakTimer *timer = [[BCWeakTimer alloc] initWithTimeInterval:timeInterval userInfo:userInfo repeats:repeats dispatchQueue:dispatchQueue];
    timer.target = target;
    timer.selector = selector;
    timer.isTarget = YES;
    return timer;
}

+ (instancetype)timerWithTimeInterval:(NSTimeInterval)timeInterval
                             userInfo:(nullable id)userInfo
                              repeats:(BOOL)repeats
                        dispatchQueue:(dispatch_queue_t)dispatchQueue
                                block:(void (^)(BCWeakTimer *timer))block {
    NSParameterAssert(block);

    BCWeakTimer *timer = [[BCWeakTimer alloc] initWithTimeInterval:timeInterval userInfo:userInfo repeats:repeats dispatchQueue:dispatchQueue];
    timer.block = block;
    timer.isTarget = NO;
    return timer;
}

///MARK: - set
- (void)setTolerance:(NSTimeInterval)tolerance
{
    @synchronized(self)
    {
        if (tolerance != _tolerance) {
            _tolerance = tolerance;

            [self resetTimerProperties];
        }
    }
}

- (NSTimeInterval)tolerance
{
    @synchronized(self)
    {
        return _tolerance;
    }
}

- (void)fire
{
    [self timerFired];
    [self resetTimerProperties];
}

- (void)timerFired
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored"-Wdeprecated-declarations"

    if (OSAtomicAnd32OrigBarrier(1, &_timerFlags.timerIsInvalidated)) {
        return;
    }

    if (OSAtomicTestAndSetBarrier(7, &_timerFlags.timerIsFired)) {
        return;
    }

    if (OSAtomicTestAndClear(7, &_timerFlags.timerIsPaused)) {
#pragma clang diagnostic pop
        self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,
                                            0,
                                            0,
                                            self.privateSerialQueue);

        __weak typeof(self) weakSelf = self;
        dispatch_source_set_event_handler(self.timer, ^{
            __strong typeof(weakSelf) self = weakSelf;
            [self timerCallback];
        });
        dispatch_source_t timer = self.timer;
        dispatch_async(self.privateSerialQueue, ^{
            dispatch_resume(timer);
        });
    }
}

- (void)invalidate
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored"-Wdeprecated-declarations"

    if (!OSAtomicTestAndSetBarrier(7, &_timerFlags.timerIsInvalidated)) {
        OSAtomicTestAndClear(7, &_timerFlags.timerIsFired);
#pragma clang diagnostic pop
        dispatch_source_t timer = self.timer;
        dispatch_async(self.privateSerialQueue, ^{
            dispatch_source_cancel(timer);
            bc_release_gcd_object(timer);
        });
    }
}

- (void)pause {
    /*
       dispatch_resume() 与 dispatch_suspend() 是平衡挂起计数的

       当一个 dispatch 的 timer 对象为挂起状态，被销毁会导致程序崩溃，所以这里采用重新创建 timer 的方式实现 pause 功能
     */
#pragma clang diagnostic push
#pragma clang diagnostic ignored"-Wdeprecated-declarations"

    if (OSAtomicAnd32OrigBarrier(1, &_timerFlags.timerIsInvalidated) || !OSAtomicAnd32OrigBarrier(1, &_timerFlags.timerIsFired)) {
        return;
    }

    if (!OSAtomicTestAndSetBarrier(7, &_timerFlags.timerIsPaused)) {
        OSAtomicTestAndClear(7, &_timerFlags.timerIsFired);
        dispatch_source_t timer = self.timer;
        dispatch_async(self.privateSerialQueue, ^{
            dispatch_source_cancel(timer);
        });
    }

#pragma clang diagnostic pop
}

///MARK: -callback

- (void)timerCallback {
#pragma clang diagnostic push
#pragma clang diagnostic ignored"-Wdeprecated-declarations"

    if (OSAtomicAnd32OrigBarrier(1, &_timerFlags.timerIsInvalidated) ||
        OSAtomicAnd32OrigBarrier(1, &_timerFlags.timerIsPaused)) {
        return;
    }

#pragma clang diagnostic pop

    if (self.isTarget) {
        if (self.target) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
            id target = self.target;
            [target performSelector:self.selector withObject:self];
#pragma clang diagnostic pop
        } else {
            [self invalidate];
        }
    } else {
        if (self.block) {
            self.block(self);
        } else {
            [self invalidate];
        }
    }

    if (!self.isRepeats) {
        [self invalidate];
    }
}

@end
